package mdt.action;

import org.tigris.gef.base.Editor;
import org.tigris.gef.base.ModeCreatePolyEdge;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.FigEdge;
import org.tigris.gef.presentation.FigPoly;
import mdt.figure.FigMessage;
import mdt.figure.FigNetworkDevice;
import mdt.model.ConnectorAnchor;
import mdt.model.IReplyMessage;

import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

public class ModeCreateMessage extends ModeCreatePolyEdge {

    private static final int DEFAULT_MESSAGE_GAP = 20;

    /**
     * The constructor.
     *
     * @param par the editor
     */
    public ModeCreateMessage(Editor par) {
        super(par);
    }

    /**
     * The constructor.
     */
    public ModeCreateMessage() {
        super();
    }

    public Fig createNewItem(MouseEvent me, int snapX, int snapY) {
        Fig fig = super.createNewItem(me, snapX, snapY);
        Object classArg = getArg("edgeClass");
        if (classArg != null && classArg.equals(IReplyMessage.class)) {
            fig.setDashed(true);
        }
        return fig;
    }

    @Override
    public void endAttached(FigEdge fe) {
        super.endAttached(fe);

        final FigMessage FigMessage = (FigMessage) fe;

        if (fe.getDestFigNode() instanceof FigNetworkDevice
                && fe.getSourceFigNode() instanceof FigNetworkDevice)
            ensureSpace(FigMessage);

        FigPoly poly = (FigPoly) fe.getFig();
        poly.setComplete(true);
    }

    protected Object getAnchorNode() {
        return new ConnectorAnchor();
    }

    /**
     * Called for a call message. Make sure there is enough space to fit the
     * return message that will be created below.
     *
     * @param FigMessage
     */
    private void ensureSpace(final FigMessage FigMessage) {
        // Make sure there is the minimum gap above the message being drawn
        final FigMessage firstMessageAbove = getNearestMessage(
                (FigNetworkDevice) getSourceFigNode(),
                FigMessage,
                false);

        if (firstMessageAbove != null) {
            final int FigMessageY = FigMessage.getFirstPoint().y;
            final int firstMessageY = firstMessageAbove.getFirstPoint().y;
            if ((FigMessageY - firstMessageY) < DEFAULT_MESSAGE_GAP) {
                FigMessage.translateEdge(
                        0,
                        firstMessageY + DEFAULT_MESSAGE_GAP - FigMessageY);
            }
        }

        // Make sure there is the minimum gap below the message being drawn
        final FigMessage firstMessageBelow = getNearestMessage(
                (FigNetworkDevice) getSourceFigNode(),
                FigMessage,
                true);

        final int heightPlusGap = DEFAULT_MESSAGE_GAP;

        if (firstMessageBelow != null
                && firstMessageBelow.getFirstPoint().y
                < FigMessage.getFirstPoint().y + heightPlusGap) {

            final int dy =
                    (FigMessage.getFirstPoint().y
                            + heightPlusGap)
                            - firstMessageBelow.getFirstPoint().y;

            for (FigMessage fig : getMessagesBelow(FigMessage)) {
                fig.translateEdge(0, dy);
                FigNetworkDevice fcr =
                        (FigNetworkDevice) fig.getDestFigNode();
                //fcr.positionHead(fig); //TODO
            }
        }
    }

    /**
     * Get a list of FigMessages below (higher Y position) than the FigMessage
     * provided.
     *
     * @param FigMessage
     * @return a list of FigMessage
     */
    private List<FigMessage> getMessagesBelow(FigMessage FigMessage) {
        final List<FigMessage> messagesBelow = new ArrayList<FigMessage>();
        for (Fig f : getEditor().getLayerManager().getContents()) {
            if (f instanceof FigMessage
                    && f != FigMessage) {
                FigMessage fm = (FigMessage) f;
                if (fm.getFirstPoint().y >= FigMessage.getFirstPoint().y) {
                    messagesBelow.add((FigMessage) f);
                }
            }
        }
        return messagesBelow;
    }

    /**
     * Get the first FigMessage below (higher Y position) the given
     * FigMessage.
     *
     * @param FigMessage
     * @return the FigMessage below or null
     */
    private FigMessage getNearestMessage(
            final FigNetworkDevice FigNetworkDevice,
            final FigMessage FigMessage,
            final boolean below) {

        FigMessage nearestMessage = null;

        for (FigEdge fe : FigNetworkDevice.getFigEdges()) {
            if (fe instanceof FigMessage && fe != FigMessage) {
                final FigMessage fm = (FigMessage) fe;
                final int y = fm.getFirstPoint().y;
                if (below) {
                    if (isBetween(y, FigMessage, nearestMessage)) {
                        nearestMessage = fm;
                    }
                } else {
                    if (isBetween(y, nearestMessage, FigMessage)) {
                        nearestMessage = fm;
                    }
                }
            }
        }

        return nearestMessage;
    }

    /**
     * Return true if a given y co-ordinate is between the two given messages
     *
     * @param val        the value to test
     * @param message1 the lowest value to check
     * @param message2 the upper value to check
     * @return
     */
    private boolean isBetween(
            final int val,
            final FigMessage message1,
            final FigMessage message2) {
        if ((message1 == null || val >= message1.getFirstPoint().y)
                && (message2 == null || val <= message2.getFirstPoint().y)) {
            return true;
        }
        return false;
    }
}
